<?hh

class A {
  public $a = 5;
  public $b;
  function __construct($b = 'foo') {
    $this->b = $b;
  }
}

class B {
  public $a = 5;
  public $b;
  function __construct($b = 'foo') {
    $this->b = $b;
  }
}

class C {
  public $a = 5;
}

class DateTime1 implements DateTimeInterface {
  public $timestamp = 0;
  function __construct($timestamp) {
    $this->timestamp = $timestamp;
  }
  function getTimestamp() {
    if ($this->timestamp <= 0) {
      throw new Exception('sneaky');
    }
    return $this->timestamp;
  }
  function diff($dt, $absolute = null) {}
  function format($format) {}
  function getTimezone() {}
  function getOffset() {}
}

class DateTime2 implements DateTimeInterface {
  function getTimestamp() {
    return 100;
  }
  function diff($dt, $absolute = null) {}
  function format($format) {}
  function getTimezone() {}
  function getOffset() {}
}

function foo(): void {}
function rfoo<reify T>(): void {}

class Bar {
  public static function foo(): void {}
  public static function rfoo<reify T>(): void {}
}

function wrapped($f) {
  try {
    $f();
  } catch (Exception $e) {
    echo 'Threw: '.$e->getMessage()."\n";
  }
}

<<__EntryPoint>>
function main(): void {
  echo "\n\n<\n\n";
  less();
  echo "\n<=\n\n";
  lesseq();
  echo "\n>\n\n";
  gt();
  echo "\n>=\n\n";
  gte();
  echo "\n<=>\n\n";
  cmp();
}


function less(): void {
  echo "!(null < false)\n";
  wrapped(() ==> !(null < false));
  echo "!(true < 0)\n";
  wrapped(() ==> !(true < 0));
  echo "!(99 < 99.0)\n";
  wrapped(() ==> !(99 < 99.0));
  echo "!(empty string < NAN)\n";
  wrapped(() ==> !('' < NAN));
  echo "!('0' < 'baz')\n";
  wrapped(() ==> !("0" < 'baz'));
  echo "!('0' < 42)\n";
  wrapped(() ==> !("0" < 42));
  echo "!('0bcd' < 1.234)\n";
  wrapped(() ==> !("0bcd" < 1.234));
  echo "!('0bcd' < 'baz')\n";
  wrapped(() ==> !("0bcd" < 'baz'));
  echo "!('0bcd' < 42)\n";
  wrapped(() ==> !("0bcd" < 42));
  echo "!('0bcd' < 1.234)\n";
  wrapped(() ==> !("0bcd" < 1.234));

  echo "!(new A() < 42)\n";
  wrapped(() ==> !(new A() < 42));
  echo "!(new A() < new A(new A()))\n";
  wrapped(() ==> !(new A() < new A(new A())));
  echo "!(new B() < new C())\n";
  wrapped(() ==> !(new B() < new C()));

  echo "!(DateTime1(1000) < new DateTime2())\n";
  wrapped(() ==> !(new DateTime1(1000) < new DateTime2()));
  echo "!(DateTime1(1000) < 42)\n";
  wrapped(() ==> !(new DateTime1(1000) < 42));

  echo "!(xml < 42)\n";
  wrapped(() ==> !(simplexml_load_string("<root />")->unknown < 42));

  echo "!(Vector < Vector)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} < Vector {5, 6, 7, 8, 9}));
  echo "!(Vector < 42)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} < 42));

  echo "Pair<string, string> < pair<string, int>\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} < Pair {'elem1', 42}));
  echo "!(Pair < 42)\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} < 42));

  echo "!(closure < 42)\n";
  wrapped(
    () ==> !(
      function() {
        return 0;
      } <
        42
    ),
  );

  echo "!(fun < 42)\n";
  wrapped(() ==> !(foo<> < 42));
  echo "!(fun < 'baz')\n";
  wrapped(() ==> !(foo<> < 'baz'));
  echo "!(rfun < 42)\n";
  wrapped(() ==> !(rfoo<int> < 42));
  echo "!(class_meth < 42)\n";
  wrapped(() ==> !(Bar::foo<> < 42));
  echo "!(class_meth < vec['foo', 'bar'])\n";
  wrapped(() ==> !(Bar::foo<> < vec['foo', 'bar']));
  echo "!(class_meth < vec['Bar', 42])\n";
  wrapped(() ==> !(Bar::foo<> < vec['Bar', 42]));
  echo "!(rclass_meth < 42)\n";
  wrapped(() ==> !(Bar::rfoo<int> < 42));

  echo "!(vec[99] < vec['foo'])\n";
  wrapped(() ==> !(vec[99] < vec['foo']));
  echo "!(vec[vec[99]] < vec[vec['foo']])\n";
  wrapped(() ==> !(vec[vec[99]] < vec[vec['foo']]));
  echo "!(vec[42] < dict[0 => 42])\n";
  wrapped(() ==> !(vec[42] < dict[0 => 42]));
  echo "!(vec[42] < dict[0 => '42'])\n";
  wrapped(() ==> !(vec[42] < dict[0 => '42']));
  echo "!(vec[42] < 42)\n";
  wrapped(() ==> !(vec[42] < 42));
  echo "!(dict[0 => !(42] < 42))\n";
  wrapped(() ==> !(dict[0 => 42] > 42));
  echo "!(keyset[42] < 42)\n";
  wrapped(() ==> !(keyset[42] < 42));

  $f1 = imagecreate(10, 10);
  echo "!(imagecreate(10, 10) < 42)\n";
  wrapped(() ==> !($f1 < 42));
  imagedestroy($f1);
}

function lesseq(): void {
  echo "!(null <= false)\n";
  wrapped(() ==> !(null <= false));
  echo "!(true <= 0)\n";
  wrapped(() ==> !(true <= 0));
  echo "!(99 <= 99.0)\n";
  wrapped(() ==> !(99 <= 99.0));
  echo "!(empty string <= NAN)\n";
  wrapped(() ==> !('' <= NAN));
  echo "!('0' <= 'baz')\n";
  wrapped(() ==> !("0" <= 'baz'));
  echo "!('0' <= 42)\n";
  wrapped(() ==> !("0" <= 42));
  echo "!('0bcd' <= 1.234)\n";
  wrapped(() ==> !("0bcd" <= 1.234));
  echo "!('0bcd' <= 'baz')\n";
  wrapped(() ==> !("0bcd" <= 'baz'));
  echo "!('0bcd' <= 42)\n";
  wrapped(() ==> !("0bcd" <= 42));
  echo "!('0bcd' <= 1.234)\n";
  wrapped(() ==> !("0bcd" <= 1.234));

  echo "!(new A() <= 42)\n";
  wrapped(() ==> !(new A() <= 42));
  echo "!(new A() <= new A(new A()))\n";
  wrapped(() ==> !(new A() <= new A(new A())));
  echo "!(new B() <= new C())\n";
  wrapped(() ==> !(new B() <= new C()));

  echo "!(DateTime1(1000) <= new DateTime2())\n";
  wrapped(() ==> !(new DateTime1(1000) <= new DateTime2()));
  echo "!(DateTime1(1000) <= 42)\n";
  wrapped(() ==> !(new DateTime1(1000) <= 42));

  echo "!(xml <= 42)\n";
  wrapped(() ==> !(simplexml_load_string("<root />")->unknown <= 42));

  echo "!(Vector <= Vector)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} <= Vector {5, 6, 7, 8, 9}));
  echo "!(Vector <= 42)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} <= 42));

  echo "Pair<string, string> <= pair<string, int>\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} <= Pair {'elem1', 42}));
  echo "!(Pair <= 42)\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} <= 42));

  echo "!(closure <= 42)\n";
  wrapped(
    () ==> !(
      function() {
        return 0;
      } <=
        42
    ),
  );

  echo "!(fun <= 42)\n";
  wrapped(() ==> !(foo<> <= 42));
  echo "!(fun <= 'baz')\n";
  wrapped(() ==> !(foo<> <= 'baz'));
  echo "!(rfun <= 42)\n";
  wrapped(() ==> !(rfoo<int> <= 42));
  echo "!(class_meth <= 42)\n";
  wrapped(() ==> !(Bar::foo<> <= 42));
  echo "!(class_meth <= vec['foo', 'bar'])\n";
  wrapped(() ==> !(Bar::foo<> <= vec['foo', 'bar']));
  echo "!(class_meth <= vec['Bar', 42])\n";
  wrapped(() ==> !(Bar::foo<> <= vec['Bar', 42]));
  echo "!(rclass_meth <= 42)\n";
  wrapped(() ==> !(Bar::rfoo<int> <= 42));

  echo "!(vec[99] <= vec['foo'])\n";
  wrapped(() ==> !(vec[99] <= vec['foo']));
  echo "!(vec[vec[99]] <= vec[vec['foo']])\n";
  wrapped(() ==> !(vec[vec[99]] <= vec[vec['foo']]));
  echo "!(vec[42] <= dict[0 => 42])\n";
  wrapped(() ==> !(vec[42] <= dict[0 => 42]));
  echo "!(vec[42] <= dict[0 => '42'])\n";
  wrapped(() ==> !(vec[42] <= dict[0 => '42']));
  echo "!(vec[42] <= 42)\n";
  wrapped(() ==> !(vec[42] <= 42));
  echo "!(dict[0 => !(42] <= 42))\n";
  wrapped(() ==> !(dict[0 => 42] > 42));
  echo "!(keyset[42] <= 42)\n";
  wrapped(() ==> !(keyset[42] <= 42));

  $f1 = imagecreate(10, 10);
  echo "!(imagecreate(10, 10) <= 42)\n";
  wrapped(() ==> !($f1 <= 42));
  imagedestroy($f1);
}

function gt(): void {
  echo "!(null > false)\n";
  wrapped(() ==> !(null > false));
  echo "!(true > 0)\n";
  wrapped(() ==> !(true > 0));
  echo "!(99 > 99.0)\n";
  wrapped(() ==> !(99 > 99.0));
  echo "!(empty string > NAN)\n";
  wrapped(() ==> !('' > NAN));
  echo "!('0' > 'baz')\n";
  wrapped(() ==> !("0" > 'baz'));
  echo "!('0' > 42)\n";
  wrapped(() ==> !("0" > 42));
  echo "!('0bcd' > 1.234)\n";
  wrapped(() ==> !("0bcd" > 1.234));
  echo "!('0bcd' > 'baz')\n";
  wrapped(() ==> !("0bcd" > 'baz'));
  echo "!('0bcd' > 42)\n";
  wrapped(() ==> !("0bcd" > 42));
  echo "!('0bcd' > 1.234)\n";
  wrapped(() ==> !("0bcd" > 1.234));

  echo "!(new A() > 42)\n";
  wrapped(() ==> !(new A() > 42));
  echo "!(new A() > new A(new A()))\n";
  wrapped(() ==> !(new A() > new A(new A())));
  echo "!(new B() > new C())\n";
  wrapped(() ==> !(new B() > new C()));

  echo "!(DateTime1(1000) > new DateTime2())\n";
  wrapped(() ==> !(new DateTime1(1000) > new DateTime2()));
  echo "!(DateTime1(1000) > 42)\n";
  wrapped(() ==> !(new DateTime1(1000) > 42));

  echo "!(xml > 42)\n";
  wrapped(() ==> !(simplexml_load_string("<root />")->unknown > 42));

  echo "!(Vector > Vector)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} > Vector {5, 6, 7, 8, 9}));
  echo "!(Vector > 42)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} > 42));

  echo "Pair<string, string> > pair<string, int>\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} > Pair {'elem1', 42}));
  echo "!(Pair > 42)\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} > 42));

  echo "!(closure > 42)\n";
  wrapped(
    () ==> !(
      function() {
        return 0;
      } >
        42
    ),
  );

  echo "!(fun > 42)\n";
  wrapped(() ==> !(foo<> > 42));
  echo "!(fun > 'baz')\n";
  wrapped(() ==> !(foo<> > 'baz'));
  echo "!(rfun > 42)\n";
  wrapped(() ==> !(rfoo<int> > 42));
  echo "!(class_meth > 42)\n";
  wrapped(() ==> !(Bar::foo<> > 42));
  echo "!(class_meth > vec['foo', 'bar'])\n";
  wrapped(() ==> !(Bar::foo<> > vec['foo', 'bar']));
  echo "!(class_meth > vec['Bar', 42])\n";
  wrapped(() ==> !(Bar::foo<> > vec['Bar', 42]));
  echo "!(rclass_meth > 42)\n";
  wrapped(() ==> !(Bar::rfoo<int> > 42));

  echo "!(vec[99] > vec['foo'])\n";
  wrapped(() ==> !(vec[99] > vec['foo']));
  echo "!(vec[vec[99]] > vec[vec['foo']])\n";
  wrapped(() ==> !(vec[vec[99]] > vec[vec['foo']]));
  echo "!(vec[42] > dict[0 => 42])\n";
  wrapped(() ==> !(vec[42] > dict[0 => 42]));
  echo "!(vec[42] > dict[0 => '42'])\n";
  wrapped(() ==> !(vec[42] > dict[0 => '42']));
  echo "!(vec[42] > 42)\n";
  wrapped(() ==> !(vec[42] > 42));
  echo "!(dict[0 => !(42] > 42))\n";
  wrapped(() ==> !(dict[0 => 42] > 42));
  echo "!(keyset[42] > 42)\n";
  wrapped(() ==> !(keyset[42] > 42));

  $f1 = imagecreate(10, 10);
  echo "!(imagecreate(10, 10) > 42)\n";
  wrapped(() ==> !($f1 > 42));
  imagedestroy($f1);
}

function gte(): void {
  echo "!(null >= false)\n";
  wrapped(() ==> !(null >= false));
  echo "!(true >= 0)\n";
  wrapped(() ==> !(true >= 0));
  echo "!(99 >= 99.0)\n";
  wrapped(() ==> !(99 >= 99.0));
  echo "!(empty string >= NAN)\n";
  wrapped(() ==> !('' >= NAN));
  echo "!('0' >= 'baz')\n";
  wrapped(() ==> !("0" >= 'baz'));
  echo "!('0' >= 42)\n";
  wrapped(() ==> !("0" >= 42));
  echo "!('0bcd' >= 1.234)\n";
  wrapped(() ==> !("0bcd" >= 1.234));
  echo "!('0bcd' >= 'baz')\n";
  wrapped(() ==> !("0bcd" >= 'baz'));
  echo "!('0bcd' >= 42)\n";
  wrapped(() ==> !("0bcd" >= 42));
  echo "!('0bcd' >= 1.234)\n";
  wrapped(() ==> !("0bcd" >= 1.234));

  echo "!(new A() >= 42)\n";
  wrapped(() ==> !(new A() >= 42));
  echo "!(new A() >= new A(new A()))\n";
  wrapped(() ==> !(new A() >= new A(new A())));
  echo "!(new B() >= new C())\n";
  wrapped(() ==> !(new B() >= new C()));

  echo "!(DateTime1(1000) >= new DateTime2())\n";
  wrapped(() ==> !(new DateTime1(1000) >= new DateTime2()));
  echo "!(DateTime1(1000) >= 42)\n";
  wrapped(() ==> !(new DateTime1(1000) >= 42));

  echo "!(xml >= 42)\n";
  wrapped(() ==> !(simplexml_load_string("<root />")->unknown >= 42));

  echo "!(Vector >= Vector)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} >= Vector {5, 6, 7, 8, 9}));
  echo "!(Vector >= 42)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} >= 42));

  echo "Pair<string, string> >= pair<string, int>\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} >= Pair {'elem1', 42}));
  echo "!(Pair >= 42)\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} >= 42));

  echo "!(closure >= 42)\n";
  wrapped(
    () ==> !(
      function() {
        return 0;
      } >=
        42
    ),
  );

  echo "!(fun >= 42)\n";
  wrapped(() ==> !(foo<> >= 42));
  echo "!(fun >= 'baz')\n";
  wrapped(() ==> !(foo<> >= 'baz'));
  echo "!(rfun >= 42)\n";
  wrapped(() ==> !(rfoo<int> >= 42));
  echo "!(class_meth >= 42)\n";
  wrapped(() ==> !(Bar::foo<> >= 42));
  echo "!(class_meth >= vec['foo', 'bar'])\n";
  wrapped(() ==> !(Bar::foo<> >= vec['foo', 'bar']));
  echo "!(class_meth >= vec['Bar', 42])\n";
  wrapped(() ==> !(Bar::foo<> >= vec['Bar', 42]));
  echo "!(rclass_meth >= 42)\n";
  wrapped(() ==> !(Bar::rfoo<int> >= 42));

  echo "!(vec[99] >= vec['foo'])\n";
  wrapped(() ==> !(vec[99] >= vec['foo']));
  echo "!(vec[vec[99]] >= vec[vec['foo']])\n";
  wrapped(() ==> !(vec[vec[99]] >= vec[vec['foo']]));
  echo "!(vec[42] >= dict[0 => 42])\n";
  wrapped(() ==> !(vec[42] >= dict[0 => 42]));
  echo "!(vec[42] >= dict[0 => '42'])\n";
  wrapped(() ==> !(vec[42] >= dict[0 => '42']));
  echo "!(vec[42] >= 42)\n";
  wrapped(() ==> !(vec[42] >= 42));
  echo "!(dict[0 => !(42] >= 42))\n";
  wrapped(() ==> !(dict[0 => 42] > 42));
  echo "!(keyset[42] >= 42)\n";
  wrapped(() ==> !(keyset[42] >= 42));

  $f1 = imagecreate(10, 10);
  echo "!(imagecreate(10, 10) >= 42)\n";
  wrapped(() ==> !($f1 >= 42));
  imagedestroy($f1);
}

function cmp(): void {

  echo "!(null <=> false)\n";
  wrapped(() ==> !(null <=> false));
  echo "!(true <=> 0)\n";
  wrapped(() ==> !(true <=> 0));
  echo "!(99 <=> 99.0)\n";
  wrapped(() ==> !(99 <=> 99.0));
  echo "!(empty string <=> NAN)\n";
  wrapped(() ==> !('' <=> NAN));
  echo "!('0' <=> 'baz')\n";
  wrapped(() ==> !("0" <=> 'baz'));
  echo "!('0' <=> 42)\n";
  wrapped(() ==> !("0" <=> 42));
  echo "!('0bcd' <=> 1.234)\n";
  wrapped(() ==> !("0bcd" <=> 1.234));
  echo "!('0bcd' <=> 'baz')\n";
  wrapped(() ==> !("0bcd" <=> 'baz'));
  echo "!('0bcd' <=> 42)\n";
  wrapped(() ==> !("0bcd" <=> 42));
  echo "!('0bcd' <=> 1.234)\n";
  wrapped(() ==> !("0bcd" <=> 1.234));

  echo "!(new A() <=> 42)\n";
  wrapped(() ==> !(new A() <=> 42));
  echo "!(new A() <=> new A(new A()))\n";
  wrapped(() ==> !(new A() <=> new A(new A())));
  echo "!(new B() <=> new C())\n";
  wrapped(() ==> !(new B() <=> new C()));

  echo "!(DateTime1(1000) <=> new DateTime2())\n";
  wrapped(() ==> !(new DateTime1(1000) <=> new DateTime2()));
  echo "!(DateTime1(1000) <=> 42)\n";
  wrapped(() ==> !(new DateTime1(1000) <=> 42));

  echo "!(xml <=> 42)\n";
  wrapped(() ==> !(simplexml_load_string("<root />")->unknown <=> 42));

  echo "!(Vector <=> Vector)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} <=> Vector {5, 6, 7, 8, 9}));
  echo "!(Vector <=> 42)\n";
  wrapped(() ==> !(Vector {0, 1, 2, 3, 4} <=> 42));

  echo "Pair<string, string> <=> pair<string, int>\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} <=> Pair {'elem1', 42}));
  echo "!(Pair <=> 42)\n";
  wrapped(() ==> !(Pair {'elem1', 'elem2'} <=> 42));

  echo "!(closure <=> 42)\n";
  wrapped(
    () ==> !(
      function() {
        return 0;
      } <=>
        42
    ),
  );

  echo "!(fun <=> 42)\n";
  wrapped(() ==> !(foo<> <=> 42));
  echo "!(fun <=> 'baz')\n";
  wrapped(() ==> !(foo<> <=> 'baz'));
  echo "!(rfun <=> 42)\n";
  wrapped(() ==> !(rfoo<int> <=> 42));
  echo "!(class_meth <=> 42)\n";
  wrapped(() ==> !(Bar::foo<> <=> 42));
  echo "!(class_meth <=> vec['foo', 'bar'])\n";
  wrapped(() ==> !(Bar::foo<> <=> vec['foo', 'bar']));
  echo "!(class_meth <=> vec['Bar', 42])\n";
  wrapped(() ==> !(Bar::foo<> <=> vec['Bar', 42]));
  echo "!(rclass_meth <=> 42)\n";
  wrapped(() ==> !(Bar::rfoo<int> <=> 42));

  echo "!(vec[99] <=> vec['foo'])\n";
  wrapped(() ==> !(vec[99] <=> vec['foo']));
  echo "!(vec[vec[99]] <=> vec[vec['foo']])\n";
  wrapped(() ==> !(vec[vec[99]] <=> vec[vec['foo']]));
  echo "!(vec[42] <=> dict[0 => 42])\n";
  wrapped(() ==> !(vec[42] <=> dict[0 => 42]));
  echo "!(vec[42] <=> dict[0 => '42'])\n";
  wrapped(() ==> !(vec[42] <=> dict[0 => '42']));
  echo "!(vec[42] <=> 42)\n";
  wrapped(() ==> !(vec[42] <=> 42));
  echo "!(dict[0 => !(42] <=> 42))\n";
  wrapped(() ==> !(dict[0 => 42] > 42));
  echo "!(keyset[42] <=> 42)\n";
  wrapped(() ==> !(keyset[42] <=> 42));

  $f1 = imagecreate(10, 10);
  echo "!(imagecreate(10, 10) <=> 42)\n";
  wrapped(() ==> !($f1 <=> 42));
  imagedestroy($f1);
}
